/*
Copyright 2008, 2009 Joonas Lehtolahti

This file is part of Openfire-C.

Openfire-C is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Openfire-C is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Openfire-C.  If not, see <http://www.gnu.org/licenses/>.
*/


#define struct_alloc(Name, Type) struct Type *Name = (struct Type *)malloc(sizeof(struct Type))

#include <stdio.h>
#include <string.h>
#include "XfirePacket.h"
#include "XfirePacketHelper.h"
#include "XfirePacketAnalyze.h"
#include "XfirePacketAnalyze.inc"
#include "packet_creation/XfirePacketsClientLogin.h"

/*
 * XfireInstance (int socketid)
 *   Creates a new Xfire Instance object to be used later
 * 
 * Parameters:
 *   socketid  network socket reserved for this connection
 * 
 * Returns:
 *   a new XFINST structure with necessary data allocated,
 *    use FreeXfireInstance function to clear this data.
 *   NULL in case of an error.
 */
DLLEXPORT(XFINST) XfireInstance(int socketid) {
	struct_alloc(xfi, StructXfireInstance);
	if (!xfi) return NULL;
	
	xfi->network = XfireNetworkInstance(socketid);
	xfi->friends = newBTree((CMPFUNC*)XfireAnalyze_FriendCMPFUNC);
	xfi->other_people = newBTree((CMPFUNC*)XfireAnalyze_FriendCMPFUNC);
	xfi->self = XfireFriend();
	xfi->groups = newBTree((CMPFUNC*)XfireAnalyze_GroupCMPFUNC);
	xfi->fav_servers = XfireFavoriteServers();
	xfi->chatrooms = newBTree((CMPFUNC*)XfireAnalyze_ChatroomCMPFUNC);
	xfi->prefs = XfireDefaultPreferences(0);
	xfi->known_clans = newBTree((CMPFUNC*)XfireAnalyze_ClanCMPFUNC);
	xfi->online_people = newBTree((CMPFUNC*)XfireAnalyze_FriendSidCMPFUNC);
	xfi->friends_favs = newBTree((CMPFUNC*)XfireAnalyze_ServerListCMPFUNC);
	xfi->game_servers = newBTree((CMPFUNC*)XfireAnalyze_ServerListCMPFUNC);
	xfi->imindex = 0;
        xfi->debug = 0;
	xfi->freedata = NULL;
	
	return xfi;
}

/*
 * XfireDefaultPreferences (unsigned int initials)
 *   Generates the integer holding default client preferences
 * 
 * Parameters:
 *   initials  The initial preferences on which to add the defaults
 * 
 * Returns:
 *   unsigned integer holding the default permission values
 */
DLLEXPORT(unsigned int) XfireDefaultPreferences(unsigned int initials) {
	unsigned int prefs = initials;
	XFP_SET_PREF(prefs, XFPREF_ENABLE_SOUNDS);
	XFP_SET_PREF(prefs, XFPREF_ENABLE_ENGROSSED_SOUNDS);
	XFP_SET_PREF(prefs, XFPREF_ENABLE_JOIN_LEAVE_SOUNDS);
	XFP_SET_PREF(prefs, XFPREF_ENABLE_JOIN_LEAVE_GROUPCHAT_SOUNDS);
	XFP_SET_PREF(prefs, XFPREF_ENABLE_VOICECHAT_CALL_SOUNDS);
	XFP_SET_PREF(prefs, XFPREF_TOOLTIP_LOGON);
	XFP_SET_PREF(prefs, XFPREF_TOOLTIP_DOWNLOADS);
	XFP_SET_PREF(prefs, XFPREF_ENABLE_SCREENSHOT_SOUNDS);
	XFP_SET_PREF(prefs, XFPREF_SHOW_FOF);
	XFP_SET_PREF(prefs, XFPREF_SHOW_OFFLINE_FRIENDS);
	return prefs;
}

/*
 * FreeXfireInstance (XFINST obj)
 *   Frees memory taken by XFINST object
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireInstance(XFINST obj) {
	if (obj) {
		FreeXfireNetworkInstance(obj->network);
		FreeXfireFriend(obj->self);
		freeBTree(obj->friends, (FREEFUNC*)FreeXfireFriend);
		freeBTree(obj->other_people, (FREEFUNC*)FreeXfireFriend);
		freeBTree(obj->groups, (FREEFUNC*)FreeXfireFriendsGroup);
		FreeXfireFavoriteServers(obj->fav_servers);
		freeBTree(obj->chatrooms, (FREEFUNC*)FreeXfireChatRoom);
		freeBTree(obj->known_clans, (FREEFUNC*)FreeXfireClan);
		freeBTree(obj->online_people, (FREEFUNC*)NULL);
		freeBTree(obj->friends_favs, (FREEFUNC*)FreeXfireGameServerList);
		freeBTree(obj->game_servers, (FREEFUNC*)FreeXfireGameServerList);
		
		free(obj);
	}
}

/*
 * XfireNetworkInstance (int socketid)
 *   Creates a new Xfire Network Instance object
 * 
 * Parameters:
 *   socketid  network socket reserved for this connection
 * 
 * Returns:
 *   XfNetworkInsatnce structure
 */
struct XfNetworkInstance *XfireNetworkInstance(int socketid) {
	struct_alloc(xfn, XfNetworkInstance);
	if (!xfn) return NULL;
	
	xfn->sock = socketid;
	xfn->network_handlers = newBTree(
		(CMPFUNC*)XfireAnalyze_NetworkHandlerListCMPFUNC);
	xfn->incoming = n0salloc(XFP_PACKET_MAXSIZE);
	xfn->in_completed = 0;
	xfn->outgoing = NULL;
	xfn->out_completed = 0;
	
	xfn->send_queue = LLcreate();
	
	xfn->readset = (fd_set*)malloc(sizeof(fd_set));
	xfn->writeset = (fd_set*)malloc(sizeof(fd_set));
	xfn->xfireset = (fd_set*)malloc(sizeof(fd_set));
	
	FD_ZERO(xfn->readset);
	FD_SET(socketid, xfn->readset);
	
	FD_ZERO(xfn->writeset);
	
	FD_ZERO(xfn->xfireset);
	FD_SET(socketid, xfn->xfireset);
	
	xfn->fdmax = socketid;
	
	return xfn;
}

/*
 * FreeXfireNetworkInstance (struct XfNetworkInstance obj)
 *   Frees memory taken by XfNetworkInstance object
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
void FreeXfireNetworkInstance(struct XfNetworkInstance *obj) {
	if (obj) {
		freeBTree(obj->network_handlers,
			(FREEFUNC*)FreeXfireNetworkHandlerList);
		n0sfree(obj->incoming);
		n0sfree(obj->outgoing);
		LLdelete(obj->send_queue, (FREEFUNC*)XfirePacketFree);
		free(obj->readset);
		free(obj->writeset);
		free(obj->xfireset);
		free(obj);
	}
}

/*
 * XfireFriend (void)
 *   Creates a new Xfire Friend object
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfFriend with initialized values
 */
DLLEXPORT(struct XfFriend*) XfireFriend(void) {
	struct_alloc(xff, XfFriend);
	if (!xff) return NULL;
	
	xff->userid = 0;
	xff->sid = n0salloc(XFP_SIZE_ATTRSIDVALUE);
    memset(xff->sid->s, '\0', XFP_SIZE_ATTRSIDVALUE);
	xff->username = NULL;
	xff->nickname = NULL;
	xff->status = n0salloc(0);
	xff->game = XfireGameData(0, 0, 0);
	xff->voice = XfireGameData(0, 0, 0);
	xff->extdata = XfireFriendExtData();
	
	xff->freedata = NULL;
	
	return xff;
}

/*
 * FreeXfireFriend (struct XfFriend *obj)
 *   Frees memory taken by XfFriend structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireFriend(struct XfFriend *obj) {
	if (obj) {
		n0sfree(obj->sid);
		n0sfree(obj->username);
		n0sfree(obj->nickname);
		n0sfree(obj->status);
		FreeXfireGameData(obj->game);
		FreeXfireGameData(obj->voice);
		FreeXfireFriendExtData(obj->extdata);
		
		free(obj);
	}
}

/*
 * XfireFavoriteServers (void)
 *   Creates a new Xfire Favorite Servers object
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfFavoriteServers with initialized values
 */
DLLEXPORT(struct XfFavoriteServers*) XfireFavoriteServers(void) {
	struct_alloc(xffs, XfFavoriteServers);
	if (!xffs) return NULL;
	
	xffs->maxfavorites = 100;
	xffs->servers = LLcreate();
	
	return xffs;
}

/*
 * FreeXfireFavoriteServers (struct XfFavoriteServers *obj)
 *   Frees memory taken by XfFavoriteServers structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireFavoriteServers(struct XfFavoriteServers *obj) {
	if (obj) {
		LLdelete(obj->servers, (FREEFUNC*)FreeXfireGameData);
		
		free(obj);
	}
}

/*
 * XfireGameData (uint gameid, uint gameip, uint gameport)
 *   Creates a new Xfire game structure
 * 
 * Parameters:
 *   gameid    the ID number of the game (0 = no game)
 *   gameip    the IP address of the server (0 = no server)
 *   gameport  the port of the server (0 = no server)
 * 
 * Returns:
 *   a pointer to new struct XfGameData with initialized values
 */
DLLEXPORT(struct XfGameData*) XfireGameData(
	unsigned int gameid, unsigned int gameip, unsigned int gameport
) {
	struct_alloc(xfgd, XfGameData);
	if (!xfgd) return NULL;
	
	xfgd->gameid = gameid;
	xfgd->ip = gameip;
	xfgd->port = gameport;
	xfgd->gameid_old = 0;
	xfgd->ip_old = 0;
	xfgd->port_old = 0;
	xfgd->extdata = XfireGameDataExt();
	
	return xfgd;
}

/*
 * FreeXfireGameData (struct XfGameData *obj)
 *   Frees memory taken by XfGameData structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireGameData(struct XfGameData *obj) {
	if (obj) {
		FreeXfireGameDataExt(obj->extdata);
		
		free(obj);
	}
}

/*
 * XfireGameDataExt (void)
 *   Creates a new Xfire extended data game structure
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfGameDataExt with initialized values
 */
DLLEXPORT(struct XfGameDataExt*) XfireGameDataExt(void) {
	struct_alloc(xfgd, XfGameDataExt);
	if (!xfgd) return NULL;
	
	xfgd->names = LLcreate();
	xfgd->values = LLcreate();
	xfgd->byfriends = NULL;
	
	return xfgd;
}

/*
 * FreeXfireGameDataExt (struct XfGameDataExt *obj)
 *   Frees memory taken by XfGameDataExt structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireGameDataExt(struct XfGameDataExt *obj) {
	if (obj) {
		LLdelete(obj->names, (FREEFUNC*)n0sfree);
		LLdelete(obj->values, (FREEFUNC*)n0sfree);
		LLdelete(obj->byfriends, NULL);
		
		free(obj);
	}
}

/*
 * XfireFriendExtData (void)
 *   Creates a new Xfire friend extended info structure
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfFriendExt with initialized values
 */
DLLEXPORT(struct XfFriendExt*) XfireFriendExtData(void) {
	struct_alloc(xff, XfFriendExt);
	if (!xff) return NULL;
	
	xff->screenshots = LLcreate();
	xff->videos = LLcreate();
	xff->clans = LLcreate();
	xff->avatartype = 0;
	xff->avatarnumber = 0;
	xff->p2p = XfireFriendP2Pinfo();
	xff->live_video = XfireBroadcast();
	
	return xff;
}

/*
 * FreeXfireFriendExtData (struct XfFriendExt *obj)
 *   Frees memory taken by XfFriendExt structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireFriendExtData(struct XfFriendExt *obj) {
	if (obj) {
		LLdelete(obj->screenshots, (FREEFUNC*)FreeXfireScreenshotInfo);
		LLdelete(obj->videos, (FREEFUNC*)FreeXfireVideoInfo);
		LLdelete(obj->clans, NULL);
		FreeXfireFriendP2Pinfo(obj->p2p);
		FreeXfireBroadcast(obj->live_video);
		
		free(obj);
	}
}

/*
 * XfireBroadcast (void)
 *   Creates a new Xfire live video broadcast structure
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfBroadcast with initialized values
 */
DLLEXPORT(struct XfBroadcast*) XfireBroadcast(void) {
	struct_alloc(xfb, XfBroadcast);
	if (!xfb) return NULL;
	
	xfb->title = NULL;
	xfb->description = NULL;
	xfb->sessionid = NULL;
	xfb->sessionimage = NULL;
	xfb->ipaddress = NULL;
	xfb->watchers = NULL;
	xfb->ipport = NULL;
	xfb->friends_only = 0;
	
	return xfb;
}

/*
 * FreeXfireBroadcast (struct XfBroadcast *obj)
 *   Frees memory taken by XfBroadcast structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireBroadcast(struct XfBroadcast *obj) {
	if (obj) {
		n0sfree(obj->title);
		n0sfree(obj->description);
		n0sfree(obj->sessionid);
		n0sfree(obj->sessionimage);
		n0sfree(obj->ipaddress);
		n0sfree(obj->watchers);
		n0sfree(obj->ipport);
		
		free(obj);
	}
}

/*
 * XfireFriendP2Pinfo (void)
 *   Creates a new Xfire friend p2p info structure
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfFriendP2P with initialized values
 */
DLLEXPORT(struct XfFriendP2P*) XfireFriendP2Pinfo(void) {
	struct_alloc(xff, XfFriendP2P);
	if (!xff) return NULL;
	
	xff->ip = 0;
	xff->port = 0;
	xff->localip = 0;
	xff->localport = 0;
	xff->status = 0;
	xff->salt = NULL;
	
	return xff;
}

/*
 * FreeXfireFriendP2Pinfo (struct XfFriendP2P *obj)
 *   Frees memory taken by XfFriendP2P structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireFriendP2Pinfo(struct XfFriendP2P *obj) {
	if (obj) {
		n0sfree(obj->salt);
		
		free(obj);
	}
}

/*
 * XfireFriendsGroup (void)
 *   Creates a new Xfire Friends Group object
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfFriendsGroup with initialized values
 */
DLLEXPORT(struct XfFriendsGroup*) XfireFriendsGroup(void) {
	struct_alloc(xffg, XfFriendsGroup);
	if (!xffg) return NULL;
	
	xffg->groupid = 0;
	xffg->name = NULL;
	xffg->users = LLcreate();
	
	return xffg;
}

/*
 * FreeXfireFriendsGroup (struct XfFriendsGroup *obj)
 *   Frees memory taken by XfFriendsGroup structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireFriendsGroup(struct XfFriendsGroup *obj) {
	if (obj) {
		n0sfree(obj->name);
		LLdelete(obj->users, (FREEFUNC*)NULL);
		
		free(obj);
	}
}

/*
 * FreeXfireFriendsGroupFull (struct XfFriendsGroup *obj)
 *   Frees memory taken by XfFriendsGroup structure, including also
 *   all the friend structs.
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireFriendsGroupFull(struct XfFriendsGroup *obj) {
	if (obj) {
		n0sfree(obj->name);
		LLdelete(obj->users, (FREEFUNC*)FreeXfireFriend);
		
		free(obj);
	}
}

/*
 * XfireChatRoom (void)
 *   Creates a new Xfire chat room object
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfChatRoom with initialized values
 */
DLLEXPORT(struct XfChatRoom*) XfireChatRoom(void) {
	struct_alloc(xfcr, XfChatRoom);
	if (!xfcr) return NULL;
	
	xfcr->gcid = NULL;
	xfcr->name = NULL;
	xfcr->motd = NULL;
	xfcr->users = newBTree((CMPFUNC*)XfireAnalyze_ChatMemberCMPFUNC);
	xfcr->voice = XfireChatRoomVoice();
	xfcr->set_saved = 0;
	xfcr->set_silenced = 0;
	xfcr->set_showjoinpart = 1;
	xfcr->set_defaultperm = 3;
	xfcr->set_protected = 0;
	xfcr->set_access = 1;
	xfcr->set_allowvoice = 1;
	xfcr->set_type = 1;
	
	xfcr->info_timestamp = 0;
	xfcr->info_usercount = 0;
	
	xfcr->self = NULL;
	
	xfcr->freedata = NULL;
	
	return xfcr;
}

/*
 * FreeXfireChatRoom (struct XfChatRoom *obj)
 *   Frees memory taken by XfChatRoom structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireChatRoom(struct XfChatRoom *obj) {
	if (obj) {
		n0sfree(obj->gcid);
		n0sfree(obj->name);
		n0sfree(obj->motd);
		freeBTree(obj->users, (FREEFUNC*)FreeXfireChatRoomUser);
		FreeXfireChatRoomVoice(obj->voice);
		
		free(obj);
	}
}

/*
 * XfireChatRoomUser (struct XfFriend *user, unsigned int permission)
 *   Creates a new Xfire chat room user object
 * 
 * Parameters:
 *   user        the user to be linked with this
 *   permission  the permission level of the user
 * 
 * Returns:
 *   a pointer to new struct XfChatRoomUser with initialized values
 */
DLLEXPORT(struct XfChatRoomUser*) XfireChatRoomUser(struct XfFriend *user, unsigned int permission) {
	struct_alloc(xfcr, XfChatRoomUser);
	if (!xfcr) return NULL;
	
	xfcr->user = user;
	xfcr->permission = permission;
	xfcr->using_voice = 0;
	xfcr->freedata = NULL;
		
	return xfcr;
}

/*
 * FreeXfireChatRoomUser (struct XfChatRoomUser *obj)
 *   Frees memory taken by XfChatRoomUser structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireChatRoomUser(struct XfChatRoomUser *obj) {
	if (obj) {
		free(obj);
	}
}

/*
 * XfireChatRoomVoice (void)
 *   Creates a new Xfire chat room voice information structure
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfChatRoomVoice with initialized values
 */
DLLEXPORT(struct XfChatRoomVoice*) XfireChatRoomVoice(void) {
	struct_alloc(xfcr, XfChatRoomVoice);
	if (!xfcr) return NULL;
	
	xfcr->session_id = NULL;
	xfcr->host_info = NULL;
	xfcr->host_type = 2;
	xfcr->voice_enabled = 0;
	xfcr->host_userid = 0;
	xfcr->max_participants = 8;
	xfcr->voice_quality = 3;
	xfcr->voice_ip = 0xcedc2897;
	xfcr->voice_port = 25200;
		
	return xfcr;
}

/*
 * FreeXfireChatRoomVoice (struct XfChatRoomVoice *obj)
 *   Frees memory taken by XfChatRoomVoice structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireChatRoomVoice(struct XfChatRoomVoice *obj) {
	if (obj) {
		n0sfree(obj->session_id);
		FreeXfireVoiceHostUser(obj->host_info);
		
		free(obj);
	}
}

/*
 * XfireVoiceHostUser (void)
 *   Creates a new Xfire group voice host user structure
 * 
 * Parameters:
 *   none
 * 
 * Returns:
 *   a pointer to new struct XfVoiceHostUser with initialized values
 */
DLLEXPORT(struct XfVoiceHostUser*) XfireVoiceHostUser(void) {
	struct_alloc(xff, XfVoiceHostUser);
	if (!xff) return NULL;
	
	xff->ip = 0;
	xff->port = 0;
	xff->localip = 0;
	xff->localport = 0;
	xff->status = 0;
	xff->salt = NULL;
	
	return xff;
}

/*
 * FreeXfireVoiceHostUser (struct XfVoiceHostUser *obj)
 *   Frees memory taken by XfVoiceHostUser structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireVoiceHostUser(struct XfVoiceHostUser *obj) {
	if (obj) {
		n0sfree(obj->salt);
		
		free(obj);
	}
}

/*
 * FreeXfireChatRoomUserFull (struct XfChatRoomUser *obj)
 *   Frees memory taken by XfChatRoomUser structure, including also
 *   the friend struct
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireChatRoomUserFull(struct XfChatRoomUser *obj) {
	if (obj) {
		FreeXfireFriend(obj->user);
		
		free(obj);
	}
}


/*
 * XfireClan (unsigned int clanid)
 *   Creates a new Xfire clan object
 * 
 * Parameters:
 *   clanid  the ID number of the clan
 * 
 * Returns:
 *   a pointer to new struct XfClan with initialized values
 */
DLLEXPORT(struct XfClan*) XfireClan(unsigned int clanid) {
	struct_alloc(xfc, XfClan);
	if (!xfc) return NULL;
	
	xfc->clanid = clanid;
	xfc->shortname = NULL;
	xfc->longname = NULL;
	xfc->members = newBTree((CMPFUNC*)XfireAnalyze_ClanMemberCMPFUNC);
	xfc->news = LLcreate();
	xfc->events = LLcreate();
	xfc->clantype = 0;
	
	return xfc;
}

/*
 * FreeXfireClan (struct XfClan *obj)
 *   Frees memory taken by XfClan structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireClan(struct XfClan *obj) {
	if (obj) {
		n0sfree(obj->shortname);
		n0sfree(obj->longname);
		freeBTree(obj->members, (FREEFUNC*)FreeXfireClanMember);
		LLdelete(obj->news, (FREEFUNC*)FreeXfireClanNews);
		LLdelete(obj->events, (FREEFUNC*)FreeXfireClanEvent);
		
		free(obj);
	}
}


/*
 * XfireClanMember (struct XfFriend *user, non0string *nick, struct XfClan *xfc)
 *   Creates a new Xfire clan member object
 * 
 * Parameters:
 *   user  the XfFriend structure this clan member is
 *   nick  the nickname of the user in this clan
 *   xfc   the clan this member information is for
 * 
 * Returns:
 *   a pointer to new struct XfClanMember with initialized values
 */
DLLEXPORT(struct XfClanMember*) XfireClanMember(struct XfFriend *user, non0string *nick, struct XfClan *xfc) {
	struct_alloc(xfcm, XfClanMember);
	if (!xfcm) return NULL;
	
	xfcm->user = user;
	xfcm->nickname = nick;
	xfcm->clan = xfc;
	
	return xfcm;
}

/*
 * FreeXfireClanMember (struct XfClanMember *obj)
 *   Frees memory taken by XfClanMember structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireClanMember(struct XfClanMember *obj) {
	if (obj) {
		n0sfree(obj->nickname);
		/* user and clan are just links, not unique data */
		
		free(obj);
	}
}

/*
 * XfireClanNews ()
 *   Creates a new Xfire clan news object
 * 
 * Parameters:
 *   newsid  The news ID number
 * 
 * Returns:
 *   a pointer to new struct XfClanNews
 */
DLLEXPORT(struct XfClanNews*) XfireClanNews(unsigned int newsid) {
	struct_alloc(xfcn, XfClanNews);
	if (!xfcn) return NULL;
	
	xfcn->newsid = newsid;
	xfcn->clan = NULL;
	xfcn->title = NULL;
	xfcn->timestamp = 0;
	xfcn->username = NULL;
	xfcn->nickname = NULL;
	xfcn->extra = NULL;
	
	return xfcn;
}

/*
 * FreeXfireClanNews (struct XfClanNews *obj)
 *   Frees memory taken by XfClanNews structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireClanNews(struct XfClanNews *obj) {
	if (obj) {
		n0sfree(obj->title);
		n0sfree(obj->username);
		n0sfree(obj->nickname);
		n0sfree(obj->extra);
		
		free(obj);
	}
}

/*
 * XfireClanEvent ()
 *   Creates a new Xfire clan event object
 * 
 * Parameters:
 *   eventid  The ID number of the event
 * 
 * Returns:
 *   a pointer to new struct XfClanNews
 */
DLLEXPORT(struct XfClanEvent*) XfireClanEvent(unsigned int eventid) {
	struct_alloc(xfce, XfClanEvent);
	if (!xfce) return NULL;
	
	xfce->eventid = eventid;
	xfce->clan = NULL;
	xfce->gameid = 0;
	xfce->title = NULL;
	xfce->starttime = 0;
	xfce->endtime = 0;
	
	return xfce;
}

/*
 * FreeXfireClanEvent (struct XfClanEvent *obj)
 *   Frees memory taken by XfClanEvent structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireClanEvent(struct XfClanEvent *obj) {
	if (obj) {
		n0sfree(obj->title);
		
		free(obj);
	}
}

/*
 * XfireScreenshotInfo (void)
 *   Creates a new Xfire screenshot info object
 * 
 * Parameters:
 *   nothing
 * 
 * Returns:
 *   a pointer to new struct XfScreenshotInfo with initialized values
 */
DLLEXPORT(struct XfScreenshotInfo*) XfireScreenshotInfo(void) {
	struct_alloc(xfs, XfScreenshotInfo);
	if (!xfs) return NULL;
	
	xfs->index = 0;
	xfs->game = NULL;
	xfs->description = NULL;
	xfs->timestamp = 0;
	xfs->guid = NULL;
	xfs->locked = 0;
	xfs->filesize = 0;
	
	return xfs;
}

/*
 * FreeXfireScreenshotInfo (struct XfScreenshotInfo *obj)
 *   Frees memory taken by XfScreenshotInfo structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireScreenshotInfo(struct XfScreenshotInfo *obj) {
	if (obj) {
		FreeXfireGameData(obj->game);
		n0sfree(obj->description);
		n0sfree(obj->guid);
		
		free(obj);
	}
}

/*
 * XfireVideoInfo (void)
 *   Creates a new Xfire video info object
 * 
 * Parameters:
 *   nothing
 * 
 * Returns:
 *   a pointer to new struct XfVideoInfo with initialized values
 */
DLLEXPORT(struct XfVideoInfo*) XfireVideoInfo(void) {
	struct_alloc(xfs, XfVideoInfo);
	if (!xfs) return NULL;
	
	xfs->index = 0;
	xfs->title = NULL;
	xfs->description = NULL;
	xfs->filesize = 0;
	xfs->res_x = 0;
	xfs->res_y = 0;
	xfs->frames = 0;
	xfs->fps = 0;
	xfs->gameid = 0;
	xfs->timestamp = 0;
	
	return xfs;
}

/*
 * FreeXfireVideoInfo (struct XfVideoInfo *obj)
 *   Frees memory taken by XfVideoInfo structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireVideoInfo(struct XfVideoInfo *obj) {
	if (obj) {
		n0sfree(obj->title);
		n0sfree(obj->description);
		
		free(obj);
	}
}

/*
 * XfireGameServerList (unsigned int gameid, LinkedList *servers)
 *   Creates a new Xfire server list object
 * 
 * Parameters:
 *   gameid   the game for which this list is for
 *   servers  the servers for the game
 * 
 * Returns:
 *   a pointer to new struct XfGameServerList with initialized values
 */
DLLEXPORT(struct XfGameServerList*) XfireGameServerList(unsigned int gameid, LinkedList *servers) {
	struct_alloc(xfs, XfGameServerList);
	if (!xfs) return NULL;
	
	xfs->gameid = gameid;
	xfs->servers = servers;
	
	return xfs;
}

/*
 * FreeXfireGameServerList (struct XfGameServerList *obj)
 *   Frees memory taken by XfGameServerList structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
DLLEXPORT(void) FreeXfireGameServerList(struct XfGameServerList *obj) {
	if (obj) {
		LLdelete(obj->servers, (FREEFUNC*)FreeXfireGameData);
		
		free(obj);
	}
}

/*
 * XfireNetworkHandlerList (uint pid)
 *   Creates a new Xfire network handler list object
 * 
 * Parameters:
 *   pid  packet id
 * 
 * Returns:
 *   a pointer to new struct XfNetworkHandlerList with initialized values
 */
struct XfNetworkHandlerList *XfireNetworkHandlerList(unsigned int pid) {
	struct_alloc(xfnh, XfNetworkHandlerList);
	if (!xfnh) return NULL;
	
	xfnh->pid = pid;
	xfnh->funcs = LLcreate();
	
	return xfnh;
}

/*
 * FreeXfireNetworkHandlerList (struct XfNetworkHandlerList *obj)
 *   Frees memory taken by XfNetworkHandler structure
 * 
 * Parameters:
 *   obj  the object to be freed
 * 
 * Returns:
 *   nothing
 */
void FreeXfireNetworkHandlerList(struct XfNetworkHandlerList *obj) {
	if (obj) {
		LLdelete(obj->funcs, (FREEFUNC*)free);
		
		free(obj);
	}
}


/*
 * XfireAnalyze_*CMPFUNC (*a, *b)
 *   CMPFUNC for Binary Tree implementation
 *  
 * Parameters:
 *   a, b  pointers to structs
 * 
 * Returns:
 *   <0 if logically a > b
 *    0 if logically a == b
 *   >0 if logically a < b
 */

int XfireAnalyze_FriendCMPFUNC(struct XfFriend *a, struct XfFriend *b) {
	return (int)(a->userid - b->userid);
}

int XfireAnalyze_FriendSidCMPFUNC(struct XfFriend *a, struct XfFriend *b) {
	return n0strcmp(a->sid, b->sid);
}

int XfireAnalyze_GroupCMPFUNC(struct XfFriendsGroup *a,
	struct XfFriendsGroup *b
) {
	return (int)(a->groupid - b->groupid);
}

int XfireAnalyze_ChatroomCMPFUNC(struct XfChatRoom *a, struct XfChatRoom *b) {
	return n0strcmp(a->gcid, b->gcid);
}

int XfireAnalyze_ClanCMPFUNC(struct XfClan *a, struct XfClan *b) {
	return (int)(a->clanid - b->clanid);
}

int XfireAnalyze_ClanMemberCMPFUNC(struct XfClanMember *a, struct XfClanMember *b) {
	return (int)(a->user->userid - b->user->userid);
}

int XfireAnalyze_ChatMemberCMPFUNC(struct XfChatRoomUser *a, struct XfChatRoomUser *b) {
	return (int)(a->user->userid - b->user->userid);
}

int XfireAnalyze_ServerListCMPFUNC(struct XfGameServerList *a, struct XfGameServerList *b) {
	return (int)(a->gameid - b->gameid);
}

int XfireAnalyze_NetworkHandlerListCMPFUNC(
	struct XfNetworkHandlerList *a, struct XfNetworkHandlerList *b
) {
	return (int)(a->pid - b->pid);
}

/*
 * XfireAnalyze_FindNetworkHandlerList (BTree *tree, uint pid)
 *   Finds network handler list by pid from tree
 * 
 * Paramters:
 *   tree  the tree to search
 *   pid   the pid to search for
 * 
 * Returns:
 *   the list object if found
 *   NULL  if not found
 */
struct XfNetworkHandlerList *XfireAnalyze_FindNetworkHandlerList(
	BTree *tree, unsigned int pid
) {
	struct XfNetworkHandlerList testlist;
	TreeNode *tnode;
	
	testlist.pid = pid;
	tnode = BTreeFind(tree, &testlist);
	if (!tnode) return NULL;
	return (struct XfNetworkHandlerList*)tnode->x;
}

/*
 * XfireSeqConnect (XFINST inst, struct sockaddr_in *address)
 *   Connects to Xfire server, pointed by address on socket s.
 * 
 * Parameters:
 *   inst      the Xfire instance
 *   address   address where Xfire server is
 * 
 * Returns:
 *   -  -2 on unknown behavior
 *   -  -1 if connection failed
 *   -   0 if connection successful
 *   -   1 if connection successful, but address pointed to old server,
 *       the contents of address will be changed to reflect the new server,
 *       the host application should NOT connect to the old address; update its records with the new one.
 */
DLLEXPORT(int) XfireSeqConnect(XFINST inst, struct sockaddr_in *address) {
	non0string *data;
	int retval;
	fd_set readset;
	struct timeval timeout;
	
	struct XfirePacket *xfpp;
	struct XfirePattr  *xfpa;
	
	int sock = inst->network->sock;
	
	if (connect(sock, (struct sockaddr*)address, sizeof(struct sockaddr_in))) {
		return -1;
	}
	
	data = char2non((unsigned const char *)"UA01");
	retval = XfSendAll(sock, data);
	n0sfree(data);
	if (retval<1) {
		return -1;
	}
	
	/* Check if the server will complain for one second */
	FD_ZERO(&readset);
	FD_SET(sock, &readset);
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;
	select(sock+1, &readset, NULL, NULL, &timeout);
	
	if (FD_ISSET(sock, &readset)) {
		retval = XfRecvAll(sock, &data, inst->network->incoming->s, &(inst->network->in_completed));
		if (retval < 1) {
			if (data) {
				n0sfree(data);
			}
			return -1;
		}
		xfpp = XfirePacketParse(data);
		n0sfree(data);
		if (!xfpp) {
			return -2;
		}
		if (xfpp->pid == 0x00af) {
			data = n0salloc(1);
			*(data->s) = 0x38;
			xfpa = XfirePacketGetAttributeByName(xfpp, data);
			n0sfree(data);
			/* FIXME: ATTENTION! THIS MIGHT NOT WORK PROPERLY, CHECK IT!!! */
			address->sin_addr = *(struct in_addr *)xfpa->attr;
			return 1;
		}
		else {
			return -2;
		}
	}
	return 0;
}

/*
 * XfireInst_DefaultConnect(XFINST inst, char *hostname)
 *   Connects to Xfire servers
 * 
 * Parameters:
 *   inst      the XFINST to use for the connection
 *   hostname  hostname to use for connection, NULL to use cs.xfire.com
 * 
 * Returns:
 *   -4 if cs.xfire.com could not be resolved
 *   -3 if invalid socket was used
 *   what XfireSeqConnect returns (TODO)
 */
DLLEXPORT(int) XfireInst_DefaultConnect(XFINST inst, char *hostname) {
	struct hostent *XfireHost;
	struct in_addr *XfireAddr;
	struct sockaddr_in XfireServerAddress;
	
	if (!hostname) hostname = "cs.xfire.com";
	XfireHost = gethostbyname(hostname);
	if (!XfireHost) {
		return -4;
	}
	XfireAddr = (struct in_addr *)XfireHost->h_addr;
	
	if (inst->network->sock == -1) {
		return -3;
	}
	
	XfireServerAddress.sin_family = AF_INET;
	XfireServerAddress.sin_port = htons(25999);
	XfireServerAddress.sin_addr = *XfireAddr;
	memset(XfireServerAddress.sin_zero, 0, sizeof(XfireServerAddress.sin_zero));
	
	return XfireSeqConnect(inst, &XfireServerAddress);
}

/*
 * XfireInst_RegisterHandler(XFINST inst, uint pid, XFNHFUNC *func, freedata)
 *   Registers a function to be called when a certain network packet
 *   is received
 * 
 * Parameters:
 *   inst      the XFINST to register it to
 *   pid       the packet id
 *   func      the function to be called
 *   freedata  data that is passed along to the callback function
 * 
 * Returns:
 *   pointer to XfNetworkHandler structure with the given data
 * 
 * Notes:
 *   if freedata is allocated pointer, it is on your responsibility to
 *   free it when the handler is unregistered or the Xfire instance freed.
 */
DLLEXPORT(struct XfNetworkHandler*) XfireInst_RegisterHandler(
	XFINST inst, unsigned int pid, void *func, void *freedata
) {
	struct XfNetworkHandlerList *list;
	struct XfNetworkHandler *xfnh;
	
	if (!inst) return NULL;
	
	/* Find the network handler list, or create if one doesn't exist */
	list = XfireAnalyze_FindNetworkHandlerList(
		inst->network->network_handlers, pid);
	if (!list) {
		list = XfireNetworkHandlerList(pid);
		BTreeAdd(inst->network->network_handlers, list);
	}
	
	/* Create new handler */
	xfnh = (struct XfNetworkHandler*)malloc(sizeof(struct XfNetworkHandler));
	xfnh->func = func;
	xfnh->freedata = freedata;
	
	/* Add the handler to the list of registered handlers */
	LLaddLast(list->funcs, xfnh);
	
	return xfnh;
}

/*
 * XfireInst_UnregisterHandler(XFINST inst, uint pid, XFNHFUNC *func, freedata)
 *   Removes function-freedata pair from list
 * 
 * Parameters:
 *   inst      the XFINST the function was registered to
 *   pid       the packet id
 *   func      the function to be called
 *   freedata  data that is passed along to the callback function
 * 
 * Returns:
 *   x  the number of instances removed
 * 
 * Notes:
 *   Remember to free freedata afterwards if allocated dynamically
 */
DLLEXPORT(unsigned int) XfireInst_UnregisterHandler(
	XFINST inst, unsigned int pid, void *func, void *freedata
) {
	struct XfNetworkHandlerList *list;
	ListNode *listnode, *nextnode;
	struct XfNetworkHandler *xfnh;
	
	unsigned int counter = 0;
	
	if (!inst) return 0;
	
	/* Find the network handler list */
	list = XfireAnalyze_FindNetworkHandlerList(
		inst->network->network_handlers, pid);
	if (!list) {
		return 0;
	}
	
	listnode = list->funcs->first;
	while (listnode) {
		nextnode = listnode->next;
		xfnh = listnode->x;
		if (xfnh->func == func && xfnh->freedata == freedata) {
			LLdelNode(listnode);
			free(xfnh);
			counter++;
		}
		listnode = nextnode;
	}
	
	return counter;
}

/*
 * XfireInst_SendPacket (XFINST inst, struct XfirePacket *packet)
 *   Adds a network packet to the queue to be sent
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   packet  the packet to be sent
 * 
 * Returns:
 *   the number of packets in queue after adding this one
 *   0 in case of error
 */
DLLEXPORT(unsigned int) XfireInst_SendPacket(XFINST inst,
	struct XfirePacket *packet
) {
	if (!inst || !packet) return 0;
	
	if (!LLaddLast(inst->network->send_queue, packet)) return 0;
	
	FD_SET(inst->network->sock, inst->network->writeset);
	
	return inst->network->send_queue->nodes;
}

/*
 * XfireInst_SendMessage (XFINST inst, struct XfFriend *xff, non0string *msg)
 *   Sends a message to a friend using the most optimal way possible
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   xff     the friend to send the message to
 *   msg     the message to send
 *           (note: the message is copied to a new allocated memory space)
 * 
 * Returns:
 *   -1  on error
 *    0  if user offline
 *    imindex  on success
 */
DLLEXPORT(int) XfireInst_SendMessage(XFINST inst,
	struct XfFriend *xff, non0string *msg
) {
	return XfireInst_SendMessageC(inst, xff, n0scopy(msg));
}

/*
 * XfireInst_SendMessageC (XFINST inst, struct XfFriend *xff, non0string *msg)
 *   Sends a message to a friend using the most optimal way possible
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   xff     the friend to send the message to
 *   msg     the message to send
 *           (note: the message is NOT copied to a new allocated memory space)
 * 
 * Returns:
 *   -1  on error
 *    0  if user offline
 *    imindex  on success
 */
DLLEXPORT(int) XfireInst_SendMessageC(XFINST inst,
	struct XfFriend *xff, non0string *msg
) {
	struct XfirePacket *packet;
	int retval = 1;
	
	if (!inst || !xff || !msg) return -1;
	

	if (XfireUtil_IsUserOnline(xff) != 1) {
		/* User offline */
		retval = 0;
	}
	else {
		retval = ++(inst->imindex);
		packet = XfirePHc_InstantMessage(n0scopy(xff->sid), retval, msg);
		/* TODO: UDP sending if appropriate */
		if (XfireInst_SendPacket(inst, packet)==0) retval = -1;
	}
	
	return retval;
}

/*
 * XfireInst_UserLogin (XFINST inst, non0string *username, non0string *password, non0string *salt, unsigned int flags)
 *   Sends login
 * 
 * Parameters:
 *   inst      the Xfire instance for the packet
 *   username  the username to use for login
 *   password  the password to use for login
 *   salt      the login salt supplied by the server
 * 
 * Returns:
 *   0  if successful
 *  -1  if error
 * 
 * Notes:
 *   copies info to new buffers, calling function must free dynamically allocated non0strings
 */
DLLEXPORT(int) XfireInst_UserLogin(XFINST inst,
	non0string *username, non0string *password, non0string *salt,
	unsigned int flags
) {
	struct XfirePacket *packet;
	int retval = 0;
	
	if (!inst || !username || !password || !salt) return -1;
	
	if (inst->self->username) n0sfree(inst->self->username);
	inst->self->username = n0scopy(username);
	
	packet = XfirePHc_UserLogin(n0scopy(username), password, salt, flags);
	if (XfireInst_SendPacket(inst, packet)==0) retval = -1;
	
	return retval;
}

/*
 * XfireInst_UserLoginSaved (XFINST inst, non0string *username, non0string *pwhash, non0string *salt, unsigned int flags)
 *   Sends login using hashed password entry
 * 
 * Parameters:
 *   inst      the Xfire instance for the packet
 *   username  the username to use for login
 *   pwhash    the password hash to use for login
 *   salt      the login salt supplied by the server
 * 
 * Returns:
 *   0  if successful
 *  -1  if error
 * 
 * Notes:
 *   copies info to new buffers, calling function must free dynamically allocated non0strings
 */
DLLEXPORT(int) XfireInst_UserLoginSaved(XFINST inst,
	non0string *username, non0string *pwhash, non0string *salt,
	unsigned int flags
) {
	struct XfirePacket *packet;
	int retval = 0;
	
	if (!inst || !username || !pwhash || !salt) return -1;
	
	if (inst->self->username) n0sfree(inst->self->username);
	inst->self->username = n0scopy(username);
	
	packet = XfirePHc_UserLoginSaved(n0scopy(username), pwhash, salt, flags);
	if (XfireInst_SendPacket(inst, packet)==0) retval = -1;
	
	return retval;
}

/*
 * XfireInst_FD_SET_read (XFINST inst, int fd)
 *   Adds a file descriptor to read set
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   fd      the file descriptor to be added
 * 
 * Returns:
 *   0 if inst == NULL
 *   1 if OK
 */
DLLEXPORT(int) XfireInst_FD_SET_read(XFINST inst, int fd) {
	if (!inst) return 0;
	
	FD_SET(fd, inst->network->readset);
	
	if (fd > inst->network->fdmax) {
		inst->network->fdmax = fd;
	}
	
	return 1;
}

/*
 * XfireInst_FD_SET_write (XFINST inst, int fd)
 *   Adds a file descriptor to write set
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   fd      the file descriptor to be added
 * 
 * Returns:
 *   0 if inst == NULL
 *   1 if OK
 */
DLLEXPORT(int) XfireInst_FD_SET_write(XFINST inst, int fd) {
	if (!inst) return 0;
	
	FD_SET(fd, inst->network->writeset);
	
	if (fd > inst->network->fdmax) {
		inst->network->fdmax = fd;
	}
	
	return 1;
}

/*
 * XfireInst_FD_CLR_read (XFINST inst, int fd)
 *   Removes a file descriptor from read set
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   fd      the file descriptor to be removed
 * 
 * Returns:
 *   0 if inst == NULL
 *   1 if OK
 */
DLLEXPORT(int) XfireInst_FD_CLR_read(XFINST inst, int fd) {
	if (!inst) return 0;
	
	FD_CLR(fd, inst->network->readset);
	
	return 1;
}

/*
 * XfireInst_FD_CLR_write (XFINST inst, int fd)
 *   Removes a file descriptor from write set
 * 
 * Parameters:
 *   inst    the Xfire instance for the packet
 *   fd      the file descriptor to be added
 * 
 * Returns:
 *   0 if inst == NULL
 *   1 if OK
 */
DLLEXPORT(int) XfireInst_FD_CLR_write(XFINST inst, int fd) {
	if (!inst) return 0;
	
	FD_CLR(fd, inst->network->writeset);
	
	return 1;
}

/*
 * XfireInst_ProcessSendAction (xfn, completed)
 *   Send processed data
 * 
 * Parameters:
 *   xfn        the Xfire network instance
 *   completed  data already sent
 * 
 * Returns:
 *  -1 on error
 *   0 if connection closed
 *   2 on success
 */
int XfireInst_ProcessSendAction(struct XfNetworkInstance *xfn,
	unsigned int completed
) {
	int result = XfireSend(xfn->sock, xfn->outgoing, completed);
	if (result > 0) {
		/* Success! */
		if (result == xfn->outgoing->l) {
			/* All data sent */
			xfn->out_completed = 0;
			n0sfree(xfn->outgoing);
			xfn->outgoing = NULL;
		} else {
			/* Data still left */
			xfn->out_completed = result;
		}
		return 2;
	}
	/* In case of an error, return the result value */
	return result;	
}


/*
 * XfireInst_CheckNetwork (XFINST inst, struct timeval *timeout, fd_set *readset, fd_set *writeset)
 *   Checks the network connections
 * 
 * Parameters:
 *   inst      the Xfire instance
 *   timeout   pointer to struct timeval specifying the timeout,
 *             NULL value to wait until something happens
 *   readset   pointer to fd_set allocated by caller
 *   writeset  pointer to fd_set allocated by caller
 * 
 * Returns:
 *   -2  Other error (message printed on stderr)
 *   -1  Xfire connection error
 *    0  Xfire connection closed
 *   >0  No errors, connection alive
 * 
 * Notes:
 *   The two supplied fd_sets will contain the file descriptors that were
 *   found to be ready for reading or writing. The fd's that are checked
 *   are those in the Xfire instance network structure.
 */
DLLEXPORT(int) XfireInst_CheckNetwork(XFINST inst, struct timeval *timeout,
	fd_set *readset, fd_set *writeset) {
	int socketfd = inst->network->sock;
	int retval;
	
	/* Get fd sets */
	*readset = *(inst->network->readset);
	*writeset = *(inst->network->writeset);
	/* Do a select on file descriptors */
	xfdbgprintf(2, ("XfireInst_CheckNetwork.select()\n"));
	retval = select(inst->network->fdmax+1, readset, writeset, NULL, timeout);
	xfdbgprintf(2, ("  retval = %d\n", retval));
	retval = 1;
	/* Check Xfire sockets
	 * TODO: UDP sockets, additional TCP sockets */
	if (FD_ISSET(socketfd, writeset)) {
		/* Data to be sent on Xfire socket */
		xfdbgprintf(2, ("XfireInst_CheckNetwork.XfireInst_ProcessSend()\n"));
		retval = XfireInst_ProcessSend(inst);
		xfdbgprintf(2, ("  retval = %d\n", retval));
	}
	if (FD_ISSET(socketfd, readset)) {
		/* Incoming data on Xfire socket */
		do {
			xfdbgprintf(2, ("XfireInst_CheckNetwork.XfireInst_ProcessRecv()\n"));
			retval = XfireInst_ProcessRecv(inst);
			xfdbgprintf(2, ("  retval = %d\n", retval));
		} while (retval & XFIH_MORE);
	}
	xfdbgprintf(2, ("XfireInst_CheckNetwork returning %d\n", retval));
	    
	return retval;
}

/*
 * XfireInst_ProcessSend (XFINST inst)
 *   Processes send queue
 * 
 * Parameters:
 *   inst    the Xfire instance
 * 
 * Returns:
 *  -2 for NULL inst
 *  -1 on error
 *   0 if connection closed
 *   1 if nothing to be sent
 *   2 on success
 * 
 * Notes:
 *   - frees the memory taken by Xfire packets in the queue
 *     after processing them
 *   - clears writeset on return value 1
 */
DLLEXPORT(int) XfireInst_ProcessSend(XFINST inst) {
	unsigned int completed;
	struct XfirePacket *packet;
	
	if (!inst) return -2;
	
	completed = inst->network->out_completed;
	if (completed) {
		/* Data sending was started on last call already */
		return XfireInst_ProcessSendAction(inst->network, completed);
	}
	
	/* No data in buffer, check queue */
	packet = (struct XfirePacket *)LLdelFirst(inst->network->send_queue);
	if (!packet) {
		/* Empty queue, clear writeset */
		FD_CLR(inst->network->sock, inst->network->writeset);
		return 1;
	}
	
	inst->network->outgoing = XfireStreamFromPacket(packet);
	
	XfirePacketFree(packet);
	
	return XfireInst_ProcessSendAction(inst->network, completed);
}

/*
 * XfireInst_ProcessRecv (XFINST inst)
 *   Processes receiving data
 * 
 * Parameters:
 *   inst    the Xfire instance
 * 
 * Returns:
 *   XFIH_FATAL               external handler returned XFNH_FATAL
 *   XFIH_PARSING_FAILED      failed packet parsing
 *   XFIH_NULL_INST           NULL inst given
 *   XFIH_SOCKET_ERROR        connection error
 *   XFIH_SOCKET_CLOSED       connection closed
 *   XFIH_PENDING             pending data
 *   XFIH_HANDLED             data handled internally = no XFNH_NOHANDLE seen
 *   XFIH_UNKNOWN             data unknown for internal handling
 *   XFIH_EXTERNAL            data handled externally
 *   XFIH_NONFATAL            internal handler encountered non-fatal error
 *   XFIH_MORE                more packets in XfireRecv queue
 *   Return values PENDING...NONFATAL may be OR-combined, then XFIH_PENDING
 *     means XFNH_NOITERATION seen.
 */
DLLEXPORT(int) XfireInst_ProcessRecv(XFINST inst) {
	unsigned int completed;
	struct XfirePacket *packet;
	non0string *data;
	struct XfNetworkInstance *xfn;
	int handler_ret;
	
	
	if (!inst) return XFIH_NULL_INST;
	
	xfn = inst->network;
	
	completed = XfireRecv(xfn->sock, &data, xfn->incoming->s, &(xfn->in_completed));
	
	if (completed <= 0) {
		/* Connection closed or error! */
		return completed;
	}
	
	if (data) {
		/* All data received */
		packet = XfirePacketParse(data);
		handler_ret = XfireInst_ProcessRecvPacket(inst, data, packet);
		XfirePacketFree(packet);
		n0sfree(data);
		if (completed == 2) {
			return handler_ret | XFIH_MORE;
		}
		else {
			return handler_ret;
		}
	}
	else {
		return XFIH_PENDING;
	}
}

/*
 * XfireInst_ProcessRecvPacket (XFINST inst)
 *   Processes receiving data
 * 
 * Parameters:
 *   inst    the Xfire instance
 * 
 * Returns:
 *   see XfireInst_ProcessRecv
 */
DLLEXPORT(int) XfireInst_ProcessRecvPacket(XFINST inst, non0string *data, struct XfirePacket *packet) {
	int handler_ret;
	XfireAnalyze_CallRawHandlers(inst, data, packet);
	
	if (!packet) {
#ifdef OPENFIRE_ERR			
		fprintf(stderr, "\nOpenfire-C: Error: Packet parsing failed! Please report!\n");
		DumpObject(data->s, data->l);
		fprintf(stderr, "\n");
#endif
		return XFIH_PARSING_FAILED;
	}
        xfdbgprintf(2, ("In: PID: 0x%04x\n", packet->pid));
	
	handler_ret = XfireAnalyze_HandleInternally(inst, packet);
#ifdef OPENFIRE_ERR			
	if (handler_ret == XFIH_FATAL) {
		fprintf(stderr, "\nOpenfire-C: External handler (by host application) returned FATAL ERROR!\n");
		fprintf(stderr, "PID: 0x%04x\n", packet->pid);
		DumpObject(data->s, data->l);
		fprintf(stderr, "\n");
	}
	if (handler_ret & XFIH_UNKNOWN) {
		fprintf(stderr, "\nOpenfire-C: Warning: Xfire protocol may have changed. Check for Openfire-C updates or report!\n");
		fprintf(stderr, "PID: 0x%04x\n", packet->pid);
		DumpObject(data->s, data->l);
		fprintf(stderr, "\n");
	}
	if (handler_ret & XFIH_NONFATAL) {
		fprintf(stderr, "\nOpenfire-C: Error: Internal handler encountered unexpected case. Please report!\n");
		fprintf(stderr, "PID: 0x%04x\n", packet->pid);
		DumpObject(data->s, data->l);
		fprintf(stderr, "\n");
	}
#endif
	
	return handler_ret;
}

/*
 * XfireAnalyze_CallRawHandlers (XFINST inst, non0string *data, struct XfirePacket *packet)
 *   Calls registered raw handlers (registered as XFPID_RAW)
 * 
 * Parameters:
 *   inst    the Xfire instance
 *   data    raw network data
 *   packet  parsed network data
 * 
 * Returns:
 *   XFIH_FATAL           external handler returned XFNH_FATAL
 *  The following return values may be combined using a bitwise OR fashion:
 *   XFIH_PENDING         all external handlers were not called
 *   XFIH_HANDLED         data handled internally = no XFNH_NOHANDLE seen
 *   XFIH_EXTERNAL        data handled externally
 */
int XfireAnalyze_CallRawHandlers (XFINST inst, non0string *data, struct XfirePacket *packet) {
	struct XfNetworkHandlerList *handlerlist = XfireAnalyze_FindNetworkHandlerList(
		inst->network->network_handlers, XFPID_RAW);
	int retval = XFIH_HANDLED;
	
	if (handlerlist) {
		typedef unsigned int XFNHFUNC_EXT(XFINST, non0string *, struct XfirePacket *, void*);
		
		ListNode *handler = handlerlist->funcs->first;
		struct XfNetworkHandler *nhandler;
		XFNHFUNC_EXT *handler_func;
		unsigned int handler_ret;
		
					
		if (handler) {
			retval |= XFIH_EXTERNAL;
		}
		while (handler) {
			nhandler = (struct XfNetworkHandler *)handler->x;
			handler_func = (XFNHFUNC_EXT *)nhandler->func;
			handler_ret = handler_func(inst, data, packet, nhandler->freedata);
			if (handler_ret & XFNH_NOHANDLE) {
				retval &= ~XFIH_HANDLED;
			}
			if (handler_ret & XFNH_NOITERATION) {
				retval |= XFIH_PENDING;
				break;
			}
			if (handler_ret & XFNH_FATAL) return XFIH_FATAL;
			handler = handler->next;
		}
	}
	return retval;
}

/*
 * XfireAnalyze_HandleInternally (XFINST inst, struct XfirePacket *packet)
 *   Internal handling of incoming packets
 * 
 * Parameters:
 *   inst    the Xfire instance
 *   packet  the Xfire network packet received
 * 
 * Returns:
 *   XFIH_FATAL           external handler returned XFNH_FATAL
 *  The following return values may be combined using a bitwise OR fashion:
 *   XFIH_PENDING         all external handlers were not called
 *   XFIH_HANDLED         data handled internally = no XFNH_NOHANDLE seen
 *   XFIH_UNKNOWN         data unknown for internal handling
 *   XFIH_EXTERNAL        data handled externally
 *   XFIH_NONFATAL        internal handler encountered non-fatal error
 */
int XfireAnalyze_HandleInternally(XFINST inst, struct XfirePacket *packet) {
	unsigned int pid = packet->pid;
	
	/* And now the boring phase... all the different packet possibilities
	 * are to be checked for and called, and the functions written also
	 * ...
	 * Non ultra descriptica */
	 switch (pid) {
		case XFPIDS_LOGINSALT:
			return XfireAnalyze_hLoginSalt(inst, packet);
		case XFPIDS_INVALIDLOGIN:
			return XfireAnalyze_hInvalidLogin(inst, packet);
		case XFPIDS_CLIENTINFO:
			return XfireAnalyze_hClientInfo(inst, packet);
		case XFPIDS_WRONGVERSION:
			return XfireAnalyze_hWrongVersion(inst, packet);
		case XFPIDS_PREFS:
			return XfireAnalyze_hPrefs(inst, packet);
		case XFPIDS_FAVORITES:
			return XfireAnalyze_hFavorites(inst, packet);
		case XFPIDS_GROUPS:
			return XfireAnalyze_hGroups(inst, packet);
		case XFPIDS_INGROUPS:
			return XfireAnalyze_hInGroups(inst, packet);
		case XFPIDS_MYSCREENSHOTS:
			return XfireAnalyze_hMyScreenshots(inst, packet);
		case XFPIDS_CONNECTIP:
			return XfireAnalyze_hConnectIP(inst, packet);
		case XFPIDS_HEARTBEAT:
			return XfireAnalyze_hHeartbeat(inst, packet);
		case XFPIDS_FRIENDS:
			return XfireAnalyze_hFriends(inst, packet);
		case XFPIDS_ONLINESTATUS:
			return XfireAnalyze_hOnlineStatus(inst, packet);
		case XFPIDS_GAMESTATUS:
			return XfireAnalyze_hGameStatus(inst, packet);
		case XFPIDS_INVITESTATUS:
			return XfireAnalyze_hInviteStatus(inst, packet);
		case XFPIDS_FRIENDINVITE:
			return XfireAnalyze_hFriendInvite(inst, packet);
		case XFPIDS_DELETEFRIEND:
			return XfireAnalyze_hDeleteFriend(inst, packet);
		case XFPIDS_FRIENDSEARCH:
			return XfireAnalyze_hFriendSearch(inst, packet);
		case XFPIDS_VOICESTATUS:
			return XfireAnalyze_hVoiceStatus(inst, packet);
		case XFPIDS_GROUPCREATE:
			return XfireAnalyze_hGroupCreate(inst, packet);
		case XFPIDS_TEXTSTATUS:
			return XfireAnalyze_hTextStatus(inst, packet);
		case XFPIDS_GAMEINFO:
			return XfireAnalyze_hGameInfo(inst, packet);
		case XFPIDS_CHANGENICK:
			return XfireAnalyze_hChangeNick(inst, packet);
		case XFPIDS_SCREENSHOTS:
			return XfireAnalyze_hScreenshots(inst, packet);
		case XFPIDS_ADVINFOUPDATE:
			return XfireAnalyze_hAdvInfoUpdate(inst, packet);
		case XFPIDS_AVATAR:
			return XfireAnalyze_hAvatar(inst, packet);
		case XFPIDS_CLANS:
			return XfireAnalyze_hClans(inst, packet);
		case XFPIDS_VIDEOS:
			return XfireAnalyze_hVideos(inst, packet);
		case XFPIDS_EGAME:
			return XfireAnalyze_hEGame(inst, packet);
		case XFPIDS_IM:
			return XfireAnalyze_hIM(inst, packet);
		case XFPIDS_BROADCAST:
			return XfireAnalyze_hBroadcast(inst, packet);
		case XFPIDS_PEERINFO:
			return XfireAnalyze_hPeerInfo(inst, packet);
		case XFPIDS_PEERINFO_OTHER:
			return XfireAnalyze_hPeerInfoOther(inst, packet);
		case XFPIDS_NEWCHANNEL:
			return XfireAnalyze_hNewChannel(inst, packet);
		case XFPIDS_MYCLANS:
			return XfireAnalyze_hMyClans(inst, packet);
		case XFPIDS_CLANMEMBERS:
			return XfireAnalyze_hClanMembers(inst, packet);
		case XFPIDS_LEFTCLAN:
			return XfireAnalyze_hLeftClan(inst, packet);
		case XFPIDS_CLANNICK:
			return XfireAnalyze_hClanNickChange(inst, packet);
		case XFPIDS_CLANORDER:
			return XfireAnalyze_hClanGroupOrder(inst, packet);
		case XFPIDS_CLANINVITE:
			return XfireAnalyze_hClanInvite(inst, packet);
		case XFPIDS_CLANEVENTS:
			return XfireAnalyze_hClanEvents(inst, packet);
		case XFPIDS_CLANEVENTDELETED:
			return XfireAnalyze_hClanEventDeleted(inst, packet);
		case XFPIDS_CLANNEWS:
			return XfireAnalyze_hClanNews(inst, packet);
		case XFPIDS_GC_LIST:
			return XfireAnalyze_hGC_List(inst, packet);
		case XFPIDS_GC_CNAME:
			return XfireAnalyze_hGC_ChangeName(inst, packet);
		case XFPIDS_GC_JOININFO:
			return XfireAnalyze_hGC_JoinInfo(inst, packet);
		case XFPIDS_GC_JOIN:
			return XfireAnalyze_hGC_Join(inst, packet);
		case XFPIDS_GC_LEAVE:
			return XfireAnalyze_hGC_Leave(inst, packet);
		case XFPIDS_GC_MESSAGE:
			return XfireAnalyze_hGC_Message(inst, packet);
		case XFPIDS_GC_INVITE:
			return XfireAnalyze_hGC_Invite(inst, packet);
		case XFPIDS_GC_CLVL:
			return XfireAnalyze_hGC_ChangeLvl(inst, packet);
		case XFPIDS_GC_REQINFO:
			return XfireAnalyze_hGC_ReqInfo(inst, packet);
		case XFPIDS_GC_KICK:
			return XfireAnalyze_hGC_Kick(inst, packet);
		case XFPIDS_GC_VOICESTATUS:
			return XfireAnalyze_hGC_VoiceStatus(inst, packet);
		case XFPIDS_GC_VOICEHOSTINFO:
			return XfireAnalyze_hGC_VoiceHostInfo(inst, packet);
		case XFPIDS_GC_VOICELEAVE:
			return XfireAnalyze_hGC_VoiceLeave(inst, packet);
		case XFPIDS_GC_VOICEJOIN:
			return XfireAnalyze_hGC_VoiceJoin(inst, packet);
		case XFPIDS_GC_ROOMINFO:
			return XfireAnalyze_hGC_RoomInfo(inst, packet);
		case XFPIDS_GC_CDEFLVL:
			return XfireAnalyze_hGC_ChangeDefaultLvl(inst, packet);
		case XFPIDS_GC_CMOTD:
			return XfireAnalyze_hGC_ChangeMOTD(inst, packet);
		case XFPIDS_GC_CALLOWVOICE:
			return XfireAnalyze_hGC_AllowVoice(inst, packet);
		case XFPIDS_GC_VOICESESSION:
			return XfireAnalyze_hGC_VoiceSession(inst, packet);
		case XFPIDS_GC_QNAME:
			return XfireAnalyze_hGC_NameQuery(inst, packet);
		case XFPIDS_GC_CPASSWORD:
			return XfireAnalyze_hGC_ChangePassword(inst, packet);
		case XFPIDS_GC_CACCESS:
			return XfireAnalyze_hGC_ChangeAccessibility(inst, packet);
		case XFPIDS_GC_INVITEDENY:
			return XfireAnalyze_hGC_DenyInvite(inst, packet);
		case XFPIDS_GC_CSILENCE:
			return XfireAnalyze_hGC_ChangeSilence(inst, packet);
		case XFPIDS_GC_CJOINPARTMSG:
			return XfireAnalyze_hGC_ChangeJoinPartMsg(inst, packet);
		case XFPIDS_FRIENDFAV:
			return XfireAnalyze_hFriendFav(inst, packet);
		case XFPIDS_SERVERS:
			return XfireAnalyze_hServers(inst, packet);
		case XFPIDS_MYVIDEOS:
			return XfireAnalyze_hMyVideos(inst, packet);
		case XFPIDS_LIVEVIDEO:
			return XfireAnalyze_hLiveVideo(inst, packet);
	 	default:
	 		return XfireAnalyze_hFallBack(inst, packet);
	 }
}


/* 
 * XfireInst_AddNewClan (XFINST inst, unsigned int clanid)
 *   Adds a new clan to known clans
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   clanid  the clanid to add
 * 
 * Returns:
 *   The new XfClan structure or NULL on error
 */
DLLEXPORT(struct XfClan*) XfireInst_AddNewClan(XFINST inst, unsigned int clanid) {
	struct XfClan *xfc;
	
	if (!inst) return NULL;
	
	xfc = XfireClan(clanid);
	
	BTreeAdd(inst->known_clans, xfc);
	
	return xfc;
}


/* 
 * XfireInst_FindClanByClanid (XFINST inst, unsigned int clanid)
 *   Finds XfClan structure by clanid
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   clanid  the clanid to search for
 * 
 * Returns:
 *   The found existing clan structure or NULL if not found
 */
DLLEXPORT(struct XfClan*) XfireInst_FindClanByClanid(XFINST inst, unsigned int clanid) {
	struct XfClan cmpclan, *xfc = NULL;
	TreeNode *result;
	cmpclan.clanid = clanid;
	
	result = BTreeFind(inst->known_clans, &cmpclan);
	
	if (result) {
		xfc = (struct XfClan *)result->x;
	}
	
	return xfc;
}

/* 
 * XfireInst_FindGroupByGroupid (XFINST inst, unsigned int groupid)
 *   Finds XfFriendsGroup structure by groupid
 * 
 * Parameters:
 *   inst     the Xfire instance to use
 *   groupid  the groupid to search for
 * 
 * Returns:
 *   The found existing group structure or NULL if not found
 */
DLLEXPORT(struct XfFriendsGroup*) XfireInst_FindGroupByGroupid(XFINST inst, unsigned int groupid) {
	struct XfFriendsGroup cmpgroup, *xfg = NULL;
	TreeNode *result;
	cmpgroup.groupid = groupid;
	
	result = BTreeFind(inst->groups, &cmpgroup);
	
	if (result) {
		xfg = (struct XfFriendsGroup *)result->x;
	}
	
	return xfg;
}


/* 
 * XfireInst_FindUserByUserid (XFINST inst, unsigned int userid)
 *   Finds XfFriend structure by userid
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   userid  the userid to search for
 * 
 * Returns:
 *   The found existing friend structure or NULL if not found
 */
DLLEXPORT(struct XfFriend*) XfireInst_FindUserByUserid(XFINST inst, unsigned int userid) {
	struct XfFriend cmpfriend, *xff = NULL;
	TreeNode *result;
	cmpfriend.userid = userid;
	
	result = BTreeFind(inst->friends, &cmpfriend);
	if (!result) {
		result = BTreeFind(inst->other_people, &cmpfriend);
	}
	
	if (result) {
		xff = (struct XfFriend *)result->x;
	}
	
	return xff;
}

/* 
 * XfireInst_FindUserByUsername (XFINST inst, non0string *username)
 *   Finds XfFriend structure by username
 * 
 * Parameters:
 *   inst      the Xfire instance to use
 *   username  the username to search for
 * 
 * Returns:
 *   The found existing friend structure or NULL if not found
 */
DLLEXPORT(struct XfFriend*) XfireInst_FindUserByUsername(XFINST inst, non0string *username) {
	struct XfFriend *xff;
	TreeNode *iterator;
	if (!inst) return NULL;
	
	if (!username) return NULL;
	
	/* Loop through friends */
	iterator = iterateTree(inst->friends);
	while (iterator) {
		xff = (struct XfFriend *)iterator->x;
		if (!xff) fprintf(stderr, "NULL friend\n");
		if (n0strcmp(xff->username, username)==0) {
			/* Got it! */
			return xff;
		}
		iterator = iterateTreeNext(iterator);
	}
	
	/* Loop through other people */
	iterator = iterateTree(inst->other_people);
	while (iterator) {
		xff = (struct XfFriend *)iterator->x;
		if (!xff) fprintf(stderr, "NULL other\n");
		if (n0strcmp(xff->username, username)==0) {
			/* Got it! */
			return xff;
		}
		iterator = iterateTreeNext(iterator);
	}
	
	/* User not found */
	return NULL;
}

/* 
 * XfireInst_FindUserBySid (XFINST inst, non0string *sid)
 *   Finds XfFriend structure by sid
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   sid     the sid to search for
 * 
 * Returns:
 *   The found existing friend structure or NULL if not found
 */
DLLEXPORT(struct XfFriend*) XfireInst_FindUserBySid(XFINST inst, non0string *sid) {
	struct XfFriend cmpfriend, *xff = NULL;
	TreeNode *result;
	cmpfriend.sid = sid;
	
	result = BTreeFind(inst->online_people, &cmpfriend);
	
	if (result) {
		xff = (struct XfFriend *)result->x;
	}
	
	return xff;
}

/* 
 * XfireInst_FindCreateChatRoom (XFINST inst, non0string *gcid)
 *   Finds XfChatRoom structure by gcid or creates one if not found
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   gcid    the gcid to search for or assign to new
 * 
 * Returns:
 *   The found existing chatroom structure or new assigned structure.
 * 
 * Notes:
 *   In case existing chat room was not found, the gcid value is COPIED,
 *   not referenced in the created chat room, so they must be freed separately.
 */
DLLEXPORT(struct XfChatRoom*) XfireInst_FindCreateChatRoom(XFINST inst, non0string *gcid) {
	struct XfChatRoom cmproom, *xfgc = NULL;
	TreeNode *result;
	cmproom.gcid = gcid;
	
	result = BTreeFind(inst->chatrooms, &cmproom);
	
	if (result) {
		xfgc = (struct XfChatRoom *)result->x;
	}
	else {
		xfgc = XfireChatRoom();
		if (xfgc) {
			xfgc->gcid = n0scopy(gcid);
			BTreeAdd(inst->chatrooms, xfgc);
		}
	}
	
	return xfgc;
}

/* 
 * XfireInst_FindChatRoom (XFINST inst, non0string *gcid)
 *   Finds XfChatRoom structure by gcid
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   gcid    the gcid to search for
 * 
 * Returns:
 *   The found existing chatroom structure or NULL if not found
 */
DLLEXPORT(struct XfChatRoom*) XfireInst_FindChatRoom(XFINST inst, non0string *gcid) {
	struct XfChatRoom cmproom, *xfgc = NULL;
	TreeNode *result;
	cmproom.gcid = gcid;
	
	result = BTreeFind(inst->chatrooms, &cmproom);
	
	if (result) {
		xfgc = (struct XfChatRoom *)result->x;
	}
	
	return xfgc;
}

/* 
 * XfireInst_FindChatRoomByName (XFINST inst, non0string *name)
 *   Finds XfChatRoom structure by room name
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   name    the chat room name
 * 
 * Returns:
 *   The found existing chatroom structure or NULL if not found
 */
DLLEXPORT(struct XfChatRoom*) XfireInst_FindChatRoomByName(XFINST inst, non0string *name) {
	struct XfChatRoom *xfgc;
	TreeNode *iterator;
	
	if (!name) return NULL;
	
	/* Loop through friends */
	iterator = iterateTree(inst->chatrooms);
	while (iterator) {
		xfgc = (struct XfChatRoom *)iterator->x;
		if (n0strcmp(xfgc->name, name)==0) {
			/* Got it! */
			return xfgc;
		}
		iterator = iterateTreeNext(iterator);
	}
	
	return NULL;
}

/* 
 * XfireInst_FindChatRoomUserByXfFriend (XFINST inst, struct XfChatRoom *xfgc, struct XfFriend *xff)
 *   Finds XfChatRoomUser structure from a chat room by XfFriend structure
 * 
 * Parameters:
 *   inst    the Xfire instance to use
 *   xfgc    the chat room structure to search in
 *   xff     the friend structure
 * 
 * Returns:
 *   The found existing chatroom user structure or NULL if not found
 */
DLLEXPORT(struct XfChatRoomUser*) XfireInst_FindChatRoomUserByXfFriend(
		XFINST inst, struct XfChatRoom *xfgc, struct XfFriend *xff) {
	struct XfChatRoomUser testuser, *xfgcu = NULL;
	TreeNode *result;
	testuser.user = xff;
	
	if (!xff) return NULL;
	
	result = BTreeFind(xfgc->users, &testuser);
	
	if (result) {
		xfgcu = (struct XfChatRoomUser *)result->x;
	}
	
	return xfgcu;
}

/* 
 * XfireInst_FindClanEventByEventid (XFINST inst, struct XfClan *xfc, unsigned int eventid)
 *   Finds XfClanEvent structure from a clan by event ID number
 * 
 * Parameters:
 *   inst     the Xfire instance to use
 *   xfc      the clan to search in
 *   eventid  the event ID number to search for
 * 
 * Returns:
 *   The found existing event structure or NULL if not found
 */
DLLEXPORT(struct XfClanEvent*) XfireInst_FindClanEventByEventid(
		XFINST inst, struct XfClan *xfc, unsigned int eventid) {
	ListNode *event_node = xfc->events->first;
	struct XfClanEvent *event;
	
	while (event_node) {
		event = (struct XfClanEvent *)event_node->x;
		if (event->eventid == eventid) return event;
		NEXT_NODE(event_node);
	}
	return NULL;
}

/* 
 * XfireUtil_AvatarURL (struct XfFriend *user)
 *   Generates URL for user avatar
 * 
 * Parameters:
 *   user  the Xfire friend whose avatar you want
 * 
 * Returns:
 *   allocated char* with the URL or NULL if no avatar
 * 
 * Notes:
 *   caller frees the data after use...
 */
DLLEXPORT(char*) XfireUtil_AvatarURL(struct XfFriend *user) {
	unsigned int at, an;
	char *url = NULL;
	unsigned char *uname;
	
	if (!user) return NULL;
	if (!user->extdata) return NULL;
	
	at = user->extdata->avatartype;
	an = user->extdata->avatarnumber;
	
	if (at == 0) return NULL;
	else if (at == 1) {
		/* Gallery avatar */
		url = (char*)malloc(sizeof(char)*71);
		sprintf(url, "http://media.xfire.com/xfire/xf/images/avatars/gallery/default/%03u.gif", an);
	}
	else if (at == 2) {
		/* Uploaded avatar */
		url = (char*)malloc(sizeof(char)*255);
		uname = non2char(user->username);
		sprintf(url, "http://screenshot.xfire.com/avatar/100/%s.jpg?%u", uname, an);
		free(uname);
	}
	else if (at == 3) {
		/* Default avatar */
		url = (char*)malloc(sizeof(char)*73);
		sprintf(url, "http://media.xfire.com/xfire/xf/images/avatars/gallery/default/xfire.gif");
	}
	return url;
}

/* 
 * XfireUtil_GetNickname (struct XfFriend *user)
 *   Gets the nickname or falls back to username of the user
 * 
 * Parameters:
 *   user  the Xfire friend whose name you want
 * 
 * Returns:
 *   existing unsigned char* with the nickname in it or NULL if impossible
 * 
 * Notes:
 *   do NOT free the data or you'll cause Segmentation Violation.
 */
DLLEXPORT(unsigned char*) XfireUtil_GetNickname(struct XfFriend *user) {
	if (!user) return NULL;
	if (!user->nickname || user->nickname->l == 0) {
		if (!user->username) return NULL;
		return user->username->s;
	}
	return user->nickname->s;
}

/* 
 * XfireUtil_IsUserOnline (struct XfFriend *user)
 *   Checks whether the user is online or offline
 * 
 * Parameters:
 *   user  the Xfire friend to check
 * 
 * Returns:
 *   -1 if NULL user was given or the user struct was invalid
 *    0 if user is offline
 *    1 if user is online
 */
DLLEXPORT(int) XfireUtil_IsUserOnline(struct XfFriend *user) {
	static unsigned char zerocmp[XFP_SIZE_ATTRSIDVALUE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	static non0string nozero;
	nozero.s = zerocmp;
	nozero.l = XFP_SIZE_ATTRSIDVALUE;
	
	if (!user) return -1;
	if (!user->sid) return -1;
	
	if (n0strcmp(user->sid, &nozero)==0) return 0;
	return 1;
}

/* 
 * XfireUtil_IPchar (unsigned int ip, unsigned char *ipchar)
 *   Converts numeric IP address into char* presentation
 * 
 * Parameters:
 *   ip      numerical IP address
 *   ipchar  pointer to allocated char buffer where the presentation will be saved
 * 
 * Returns:
 *   ipchar
 */
DLLEXPORT(unsigned char *) XfireUtil_IPchar(unsigned int ip, unsigned char *ipchar) {
	sprintf((char*)ipchar, "%u.%u.%u.%u", (ip&0xff000000)>>24, (ip&0x00ff0000)>>16, (ip&0x0000ff00)>>8, (ip&0x000000ff));
	return ipchar;
}

/* 
 * XfireUtil_IPvalue (unsigned char *ipchar)
 *   Converts xxx.xxx.xxx.xxx IP address into unsigned integer value
 * 
 * Parameters:
 *   ipchar  pointer to the IP string
 * 
 * Returns:
 *   the string converted to number, if the string is not valid, result is undefined
 */
DLLEXPORT(unsigned int) XfireUtil_IPvalue(unsigned char *ipchar) {
	unsigned int a,b,c,d;
	sscanf((char*)ipchar, "%u.%u.%u.%u", &a, &b, &c, &d);
	return (a << 24) | (b << 16) | (c << 8) | (d);
}
